#include "Divider.h"
#include "types.h"
#include "verilated.h"
#include <cstdint>
#include <cstdio>
#include <iostream>
#include <utility>

Divider::Divider() {
    ctx = new VerilatedContext;

    top = new Top{ctx};

#ifdef CONFIG_VTRACE
    ctx->traceEverOn(true);
    tfp = new VerilatedVcdC;
    top->trace(tfp, 99);
    tfp->open("vtrace.vcd");
#endif
}

Divider::~Divider() {
#ifdef CONFIG_VTRACE
    tfp->dump(ctx->time());
    tfp->close();
    delete tfp;
#endif
    top->final();
    delete top;
    delete ctx;
}

void Divider::step() {
    top->eval();
#ifdef CONFIG_VTRACE
    tfp->dump(ctx->time());
    ctx->timeInc(1);
#endif
}

void Divider::single_cycle() {
    top->clock = 0; step();
    top->clock = 1; step();
}

void Divider::reset() {
    top->reset = 1;
    for (int i = 0; i < 5; ++i) {
        single_cycle();
    }
    top->reset = 0;
#ifdef CONFIG_ISA64
    top->word = 0; /// ????
#else
    top->word = 1;
#endif
}

std::pair<Package, Package> Divider::UU(UInt x, UInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->sign = 0;
    while (!top->done) {
        single_cycle();
    }
    Package answer;
    answer.quotient = UInt(x) / UInt(y);
    answer.remainder = UInt(x) % UInt(y);
    Package result;
    result.quotient = UInt(top->q);
    result.remainder = UInt(top->r);
    return std::make_pair(result, answer);
}

std::pair<Package, Package> Divider::US(UInt x, SInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->sign = 1;
    while (!top->done) {
        single_cycle();
    }
    Package answer;
    answer.quotient = SIntD(x) / SIntD(y);
    answer.remainder = SIntD(x) % SIntD(y);
    Package result;
    result.quotient = UInt(top->q);
    result.remainder = UInt(top->r);
    return std::make_pair(result, answer);
}

std::pair<Package, Package> Divider::SU(SInt x, UInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->sign = 1;
    while (!top->done) {
        single_cycle();
    }
    Package answer;
    answer.quotient = SIntD(x) / SIntD(y);
    answer.remainder = SIntD(x) % SIntD(y);
    Package result;
    result.quotient = UInt(top->q);
    result.remainder = UInt(top->r);
    return std::make_pair(result, answer);
}

std::pair<Package, Package> Divider::SS(SInt x, SInt y) {
    top->valid = 1;
    while (!top->ready) {
        single_cycle();
    }
    top->x = x;
    top->y = y;
    top->sign = 1;
    while (!top->done) {
        single_cycle();
    }
    Package answer;
    answer.quotient = SIntD(x) / SIntD(y);
    answer.remainder = SIntD(x) % SIntD(y);
    Package result;
    result.quotient = UInt(top->q);
    result.remainder = UInt(top->r);
    return std::make_pair(result, answer);
}
